//
// Created by xinrong.qian on 2025/5/29.
//

#include <iostream>
#include <opencv2/highgui.hpp>
#include "processor/CardPickProcessor.h"
#include "processor/CTLineResult.h"
#include "processor/QRPickDetector.h"

/********************************************************/
// TcResponseInfo封装错误处理逻辑
TcResponseInfo
CardPickProcessor::ErrorResponseInfo(const std::string& errorMsg) {
    std::cerr << errorMsg << std::endl;
    return {errorMsg, "", {}, 0, 0, 0, 0, 0};
}

// TcResponseInfo封装成功返回函数
TcResponseInfo
CardPickProcessor::SuccessResponseInfo(const std::string& successMsg, const std::string& tcResult,
                                   const std::vector<double>& grayValues, int pc, int pt,
                                   double cValue, double tValue, double tcRatio) {
    return {successMsg, tcResult, grayValues, pc, pt, cValue, tValue, tcRatio};
}

std::string
CardPickProcessor::calculateTcResult(double tcRatio) {
    if (tcRatio == -1 || tcRatio == 0 || tcRatio == 0.01) {
        return "无效"; // 无效
    } else if (tcRatio < 0.85) {
        return "阳性"; // 阳性
    } else if (tcRatio <= 1.0) {
        return "弱阳性"; // 弱阳性
    } else {
        return "阴性"; // 阴性
    }
}


cv::Mat
CardPickProcessor::correctAndCropWhiteRectangle(const cv::Mat &input) {
    // 直接返回原始图像如果输入无效
    if (input.empty())  {
        return input.clone();  // 返回深拷贝
    }

    // 创建处理用的副本
    cv::Mat processed = input.clone();
    cv::Mat grey;
    cv::cvtColor(processed, grey, cv::COLOR_BGR2GRAY);

    // 二值化处理
    cv::threshold(grey, grey, 0, 255, cv::THRESH_BINARY | cv::THRESH_OTSU);

    // 形态学开运算去除小噪点
    {
        cv::Mat kernel = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(5, 5));
        cv::morphologyEx(grey, grey, cv::MORPH_OPEN, kernel);
        // kernel自动释放
    }

    // 寻找轮廓
    std::vector<std::vector<cv::Point>> contours;
    std::vector<cv::Vec4i> hierarchy;
    cv::findContours(grey, contours, hierarchy, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE);

    // 寻找符合条件的最大轮廓
    auto isValidContour = [](const std::vector<cv::Point>& contour) {
        cv::Rect rect = cv::boundingRect(contour);
        return rect.width  < rect.height;  // 只考虑宽度小于高度的轮廓
    };

    auto it = std::max_element(contours.begin(),  contours.end(),
                               [&](const std::vector<cv::Point>& a, const std::vector<cv::Point>& b) {
                                   return !isValidContour(a) || (isValidContour(b) && cv::contourArea(a) < cv::contourArea(b));
                               });

    if (it != contours.end()  && isValidContour(*it)) {
        // 获取最小外接旋转矩形
        cv::RotatedRect rotatedRect = cv::minAreaRect(*it);

        // 计算旋转角度（确保高度大于宽度）
        float angle = rotatedRect.angle;
        if (rotatedRect.size.width  > rotatedRect.size.height)  {
            angle += 90.0f;
        }

        // 获取旋转矩阵
        cv::Mat rotMatrix = cv::getRotationMatrix2D(rotatedRect.center,  angle, 1.0);

        // 旋转图像
        cv::Mat rotated;
        cv::warpAffine(processed, rotated, rotMatrix, processed.size(),
                       cv::INTER_LINEAR, cv::BORDER_CONSTANT, cv::Scalar(0,0,0));

        // 计算旋转后的边界框
        std::vector<cv::Point2f> rectPoints(4);
        rotatedRect.points(rectPoints.data());

        std::vector<cv::Point2f> transformedPoints;
        cv::transform(rectPoints, transformedPoints, rotMatrix);

        cv::Rect bbox = cv::boundingRect(transformedPoints);
        cv::Rect safeROI = bbox & cv::Rect(0, 0, rotated.cols,  rotated.rows);

        // 返回裁剪后的图像
        return rotated(safeROI).clone(); // 确保返回的是独立拷贝
    }

    // 没有找到有效轮廓，返回原始图像副本
    return input.clone();
}

/**
 * 计算无旋转矩形区域的像素值之和
 * @param gray 输入灰度图
 * @param center 矩形中心点坐标 (x,y)
 * @param width 矩形宽度
 * @param height 矩形高度
 * @return 矩形区域内像素值总和，-1表示出错
 */
double
CardPickProcessor::calculateRectSum(const cv::Mat &gray,
                                    const cv::Point2f &center,
                                    double width,
                                    double height){
    // 计算矩形边界（确保不越界）
    int x1 = static_cast<int>(center.x - width/2);
    int y1 = static_cast<int>(center.y - height/2);
    int x2 = static_cast<int>(center.x + width/2);
    int y2 = static_cast<int>(center.y + height/2);

    // 边界检查
    x1 = std::max(0, x1);
    y1 = std::max(0, y1);
    x2 = std::min(gray.cols  - 1, x2);
    y2 = std::min(gray.rows  - 1, y2);

    // 直接计算ROI区域的和
    if (x2 <= x1 || y2 <= y1) {
        return 0; // 无效区域
    }

    cv::Mat roi = gray(cv::Rect(x1, y1, x2 - x1 + 1, y2 - y1 + 1));
    return cv::sum(roi)[0];
}

/**
 * 绘制无旋转矩形
 * @param image 输入图片
 * @param center 矩形中心点坐标 (x,y)
 * @param width 矩形宽度
 * @param height 矩形高度

*/
void
CardPickProcessor::visualizeRect(cv::Mat& image,
                                 const cv::Point2f &center,
                                 cv::Scalar_<double> color,
                                 double width,
                                 double height){
    //  * @param color 矩形颜色
    // * @param thickness 矩形线条宽度
    int thickness = 1;

    cv::Point pt1(center.x - width/2, center.y - height/2);
    cv::Point pt2(center.x + width/2, center.y + height/2);
    cv::rectangle(image, pt1, pt2, color, thickness);
}

double
CardPickProcessor::calculateRectGraySum(const cv::Mat& image, const cv::Point& center, int width, int height, bool invert) {
    // 检查长宽是否为奇数
    if (width % 2 == 0 || height % 2 == 0) {
        std::cerr << "Error: 长和宽都必须是奇数（如 3, 5, 7...）" << std::endl;
        return -1.0;
    }

    int half_width = width / 2;  // 宽度半径
    int half_height = height / 2; // 高度半径
    int x = center.x;
    int y = center.y;

    // 边界检查
    if (x - half_width < 0 || x + half_width >= image.cols  ||
        y - half_height < 0 || y + half_height >= image.rows)  {
        std::cerr << "Error: 矩形区域超出图像边界" << std::endl;
        return -1.0;
    }

    double sum = 0.0;
    for (int i = -half_height; i <= half_height; ++i) {
        for (int j = -half_width; j <= half_width; ++j) {
            uchar pixel = image.at<uchar>(y  + i, x + j);
            sum += invert ? (255 - pixel) : pixel;
        }
    }
    return sum;
}

std::vector<double>
CardPickProcessor::calculateRowSums(const cv::Mat& image, bool invert) {
    // 检查是否为单通道灰度图
    if (image.channels()  != 1) {
        std::cerr << "Error: 输入图像必须是单通道灰度图" << std::endl;
        return {};
    }

    std::vector<double> rowSums;
    rowSums.reserve(image.rows);  // 预分配空间

    for (int y = 0; y < image.rows;  ++y) {
        double sum = 0.0;
        const uchar* rowPtr = image.ptr<uchar>(y);  // 获取当前行的指针

        for (int x = 0; x < image.cols;  ++x) {
            uchar pixel = rowPtr[x];
            sum += invert ? (255 - pixel) : pixel; // 根据 invert 决定是否反转
        }

        rowSums.push_back(sum);
    }

    return rowSums;
}


int
CardPickProcessor::calculateGraySumAtPoints(const cv::Mat& image,
                             const std::vector<cv::Point>& points, bool invert) {
    if (image.empty()  || points.size()  != 4) {
        throw std::invalid_argument("Invalid input");
    }

    // 转换为灰度图（同上）
    cv::Mat grayImage;
    if (image.channels()  == 3) {
        cv::cvtColor(image, grayImage, cv::COLOR_BGR2GRAY);
    } else {
        grayImage = image;
    }

    // 创建多边形掩模
    cv::Mat mask = cv::Mat::zeros(grayImage.size(),  CV_8UC1);
    std::vector<std::vector<cv::Point>> contours = {points};
    cv::fillPoly(mask, contours, cv::Scalar(255));

    // 计算掩模区域内的灰度值和
    cv::Scalar sum = cv::sum(grayImage & mask);

    // 反转处理
    if (invert) {
        cv::Scalar pixelCount = cv::sum(mask) / 255;
        return 255 * pixelCount[0] - sum[0];
    }

    grayImage.release();
    mask.release();

    return sum[0];
}

// 绘制折线图
void
CardPickProcessor::drawCurve(const std::vector<double> &data, std::string &name) {
    if (data.empty()) {
        std::cout << "输入的数据为空，无法绘制曲线。" << std::endl;
        return;
    }

    cv::Mat image = cv::Mat::zeros(500, 800, CV_8UC3);

    int width = image.cols;
    int height = image.rows;

    // 找到数据的最大值和最小值
    double minVal, maxVal;
    cv::minMaxLoc(data, &minVal, &maxVal);

    // 计算坐标映射的比例
    double scaleX = static_cast<double>(width) / (data.size() - 1);
    double scaleY = static_cast<double>(height) / (maxVal - minVal);

    // 绘制曲线
    for (size_t i = 0; i < data.size() - 1; ++i) {
        int x1 = static_cast<int>(i * scaleX);
        int y1 = height - static_cast<int>((data[i] - minVal) * scaleY);
        int x2 = static_cast<int>((i + 1) * scaleX);
        int y2 = height - static_cast<int>((data[i + 1] - minVal) * scaleY);

        cv::line(image, cv::Point(x1, y1), cv::Point(x2, y2), cv::Scalar(0, 255, 0), 2);
    }

    // 显示图像
    cv::imshow(name, image);
}


// 声明扫描图像均值计算
std::vector<double>
CardPickProcessor::meanRoundRectangle(const cv::Mat &imgGray, int R) {
    int width = imgGray.cols;
    int height = imgGray.rows;

    std::vector<double> outVector;

    // 目标是在整个高度范围内检测每一行，
    // 对每一行中的每个点进行半径 R 的画圈，只计算圆内的区域面积，然后对每一行的结果求和。
    // 最后，继续处理下一行，直到处理完所有行。
    /*************************开始扫描**************************/
    for (int a = 0; a < height; ++a) {
        int sum_pix = 0;
        int pix_counts = 0;

        int i_min = std::max(a - R, 0);
        int i_max = std::min(a + R, height - 1);

        // 规定height方向范围
        for (int i = i_min; i <= i_max; ++i) {
            int dy = i - a;
            int dy2 = dy * dy;

            // 规定width方向范围
            int j_min = std::max(0, R - std::abs(dy));
            int j_max = std::min(width - 1, width - R + std::abs(dy));

            for (int j = j_min; j <= j_max; ++j) {
                int dx = j - R;
                int r2 = dx * dx + dy2;

                if (r2 <= R * R) {
                    sum_pix += 255 - imgGray.at<uchar>(i, j);
                    ++pix_counts;
                }
            }
        }

        if (pix_counts > 0) {
            outVector.push_back(static_cast<double>(sum_pix) / static_cast<double>(pix_counts));
        } else {
            outVector.push_back(0.0);  // 如果没有像素点在圆内，添加0.0
        }
    }
    /*************************结束扫描**************************/
    return outVector;
}

/********************************************************/

/**
 * 请求返回函数
 *
 * @param input
 * @param x
 * @param y
 * @param width
 * @param height
 * @return TcResponseInfo
 */
TcResponseInfo CardPickProcessor::GetTcResponseInfo(const cv::Mat &input, int x, int y, int width, int height) {
    if (input.empty())  {
        std::cerr << "The input image is empty" << std::endl;
        return ErrorResponseInfo("The input image is empty");
    }


    // 裁减图像
    cv::Mat image = correctAndCropWhiteRectangle(input);
    if (image.empty()){
        return ErrorResponseInfo("The input image is empty");
    }

//    if (debugFlag){
//        cv::imshow("image", image);
////        cv::waitKey(0);
//    }

    // 多个单卡 - 借用单卡参数
    std::vector<CTLineResult> resultsArr;
    const CardParams defaultCard = {
            .card_height = 700,
            .card_width = 200,
            .channel_height = 145,
            .channel_width = 25,
            .qr_side_length = 125,
            .qr_to_c = 122 + 8,
            .top_to_c = 56,

            .c_to_t = 50 + 5
    };

    try {
        // 创建QRPickDetector操作对象
        QRPickDetector qrDetector;

        // 检查图像中是否包含QR码
        if (!qrDetector.isContainsQRCode(image)) {
            std::cerr << "No QR code detected" << std::endl;
            return ErrorResponseInfo("No QR code detected");
        }

        // 生成QR计算的相关内部参数信息
        qrDetector.generateQRParams();
        const auto& qrContents = qrDetector.getQrContents();
        const size_t numQRCodes = qrContents.size(); // 获取QR码数量

        // 预分配内存
        std::vector<CardParams> cardParamsArr(numQRCodes, defaultCard);
        std::vector<double> qrFactorValues; // 存储缩放系数
        qrFactorValues.reserve(numQRCodes);

        // 计算缩放系数
        for (size_t i = 0; i < numQRCodes; ++i) {
            qrFactorValues.push_back(qrDetector.getScaleFactorValue(defaultCard.qr_side_length,  i));
        }

        // 应用缩放系数
        for (size_t i = 0; i < numQRCodes; ++i) {
            CardParams& params = cardParamsArr[i];
            const double factor = qrFactorValues[i];

            params.card_height  *= factor;
            params.card_width  *= factor;
            params.channel_height  *= factor;
            params.channel_width  *= factor;
            params.qr_side_length  *= factor;
            params.qr_to_c  *= factor;
            params.top_to_c  *= factor;
            params.c_to_t *= factor;

            // 打印逻辑函数
            std::cout << "-----------------------------New Params-------------------------------\n"
                      << "card_height: " << params.card_height  << "\n"
                      << "card_width: " << params.card_width  << "\n"
                      << "channel_height: " << params.channel_height  << "\n"
                      << "channel_width: " << params.channel_width  << "\n"
                      << "qr_side_length: " << params.qr_side_length  << "\n"
                      << "qr_to_c: " << params.qr_to_c  << "\n"
                      << "top_to_c: " << params.top_to_c  << "\n"
                      << "c_to_t: " << params.c_to_t << "\n"
                      << "------------------------------------------------------------\n";
        }

        // 处理ROI区域
        std::vector<cv::Mat> roiMats;
        roiMats.reserve(numQRCodes);
        for (size_t i = 0; i < numQRCodes; ++i) {
            const CardParams& params = cardParamsArr[i];
            const auto& qrCorners = qrDetector.getAllQrCorners()[i];

            const double qrBottomToChannelCenter = (params.qr_to_c - params.top_to_c)  + params.channel_height  / 2.0;
            qrDetector.processSingleQR(qrCorners,  qrBottomToChannelCenter, params.channel_width,  params.channel_height);

            roiMats.push_back(qrDetector.getExtractedROIs(i));
            if (debugFlag) {
                cv::imshow("ROI " + std::to_string(i + 1), roiMats.back());
            }
        }

        if (debugFlag) {
            cv::imshow("Marked QR Code", qrDetector.getMarkedImage());
        }

        // 计算矩形框参数
        std::vector<std::vector<cv::Point>> allPoints;
        std::vector<std::vector<int>> boxSides;
        const int boxNum = 4;
        allPoints.reserve(numQRCodes * boxNum);
        boxSides.reserve(numQRCodes);

        for (size_t i = 0; i < numQRCodes; ++i) {
            const CardParams& params = cardParamsArr[i];
            int boxSideWidth = static_cast<int>(params.channel_width  / 2.0); // 矩形框的边长 与设置检测条卡宽度 1/2
            int boxSideHeight = static_cast<int>(params.channel_width  / 4.0); // 矩形框的边长 与设置检测条卡宽度 1/4

            // 确保奇数边长
            boxSideWidth += (boxSideWidth % 2 == 0) ? 1 : 0;
            boxSideHeight += (boxSideHeight % 2 == 0) ? 1 : 0;

            const double centerX = params.channel_width  / 2.0;
            const double bgToBg = params.c_to_t / 3.0; // 背景到背景的距离 均分CT线间距距离

            // 计算偏移量
            std::vector<double> offsets = {
                    params.qr_to_c,
                    params.qr_to_c + params.c_to_t,
                    params.qr_to_c + bgToBg,
                    params.qr_to_c + 2 * bgToBg
            };

            const auto& qrCorners = qrDetector.getAllQrCorners()[i];
            // 放置矩形框中心点坐标
            for (size_t a = 0; a < offsets.size(); ++a) {
                std::vector<cv::Point> roiCorners = qrDetector.processSingleQROffInstance(qrCorners, offsets[a], boxSideWidth,  boxSideHeight,a);
                allPoints.push_back(roiCorners);
            }

            boxSides.push_back({boxSideWidth,  boxSideHeight}); // 矩形框的宽 高
            // 打印逻辑函数
            std::cout << "-----------------------------boxSides&centerPoints " << (i + 1) << "-------------------------------\n"
                      << "boxSideWidth: " << boxSideWidth  << "\n"
                      << "boxSideHeight: " << boxSideHeight  << "\n"
                      << "centerX: " << centerX  << "\n"
                      << "bgToBg: " << bgToBg << "\n"
                      << "------------------------------------------------------------\n";
        }

        // 计算
        for (size_t i = 0; i < numQRCodes; ++i) {
            std::vector<std::vector<cv::Point>> points;
            for(int a = 0; a < boxNum; ++a){
                points.push_back(allPoints[i * boxNum + a]);
            }
            const auto& sides = boxSides[i];

            // 计算矩形框内灰度值和
            const double pixelSumC = calculateGraySumAtPoints(image, points[0]);
            const double pixelSumT = calculateGraySumAtPoints(image, points[1]);
            const double pixelSumBg1 = calculateGraySumAtPoints(image, points[2]);
            const double pixelSumBg2 = calculateGraySumAtPoints(image, points[3]);

            std::cout << "-----------------------------pixelSum " << (i + 1) << "-------------------------------\n"
                      << "pixelSumC: " << pixelSumC  << "\n"
                      << "pixelSumT: " << pixelSumT  << "\n"
                      << "pixelSumBg1: " << pixelSumBg1  << "\n"
                      << "pixelSumBg2: " << pixelSumBg2 << "\n"
                      << "------------------------------------------------------------\n";

            if (pixelSumC == -1 || pixelSumT == -1 || pixelSumBg1 == -1 || pixelSumBg2 == -1) {
                throw std::runtime_error("Invalid rectangle size error");
            }

            // 计算T/C比值
            const double bgMean = (pixelSumBg1 + pixelSumBg2) / (2 * sides[0] * sides[1]) * 1; // 背景单个像素点的背地底值
            const double tValue = std::max(0.0, pixelSumT - bgMean * sides[0] * sides[1]);
            const double cValue = std::max(0.0, pixelSumC - bgMean * sides[0] * sides[1]);
            const double tcRatio = (cValue == 0) || (tValue == 0) ? -1 : tValue / cValue; // 计算T/C比值


            // 计算灰度曲线 - 一行数据为一个点位值
            cv::Mat roiMatGrey;
            cv::cvtColor(roiMats[i], roiMatGrey, cv::COLOR_BGR2GRAY);
            std::vector<double> grayValues = calculateRowSums(roiMatGrey);
            roiMatGrey.release();
            for (auto& val : grayValues) {
                val -= bgMean * sides[0]; // 减去一行的背底值 - 获取新曲线
            }

            //  构建返回结果
            CTLineResult result = {
                    .tcResult = calculateTcResult(tcRatio), // 计算T/C结果 阴阳性
                    .grayValues = grayValues,
                    .pc = static_cast<int>(cardParamsArr[i].top_to_c),
                    .pt = static_cast<int>(cardParamsArr[i].top_to_c + cardParamsArr[i].c_to_t),
                    .cValue = std::round(cValue * 100.0) / 100.0,
                    .tValue = std::round(tValue * 100.0) / 100.0,
                    .tcRatio =  std::round(tcRatio * 100.0) / 100.0
            };

            resultsArr.push_back(result);

            // 打印逻辑函数
            std::cout << "-----------------------------results " << ( i + 1 ) << "-------------------------------\n" << std::endl;
            std::cout << "bgMean: " << bgMean << std::endl;
            std::cout << "cValue: " << result.cValue << std::endl;
            std::cout << "tValue: " << result.tValue << std::endl;
            std::cout << "tcRatio: " << result.tcRatio << std::endl;
            std::cout << "tcResult: " << result.tcResult << std::endl;
            std::cout << "pc: " << result.pc << std::endl;
            std::cout << "pt: " << result.pt << std::endl;
            std::cout << "grayValues: [";
            for (size_t m = 0;  m < result.grayValues.size(); ++m) {
                std::cout << static_cast<int>(result.grayValues[m]);
                if ( m != result.grayValues.size() - 1) {
                    std::cout << ", ";
                }
            }
            std::cout << "]," << std::endl;
            std::cout << "-----------------------------results-------------------------------\n" << std::endl;

            // 所有检测区域+QR区域显示
            if (debugFlag) {
                cv::Mat qrImage = qrDetector.getMarkedImage();

                //旋转90度
                cv::rotate(qrImage, qrImage, cv::ROTATE_90_CLOCKWISE);
                cv::imshow("Marked QR Code", qrImage);

                // 绘制灰度曲线
                std::string windowName = "Curve " + std::to_string(i + 1);
                drawCurve(grayValues, windowName);
            }

        }

        if (debugFlag) {
            cv::waitKey(0);
        }

        return SuccessResponseInfo(
                "Success",
                resultsArr[0].tcResult,
                resultsArr[0].grayValues,
                resultsArr[0].pc,
                resultsArr[0].pt,
                resultsArr[0].cValue,
                resultsArr[0].tValue,
                resultsArr[0].tcRatio
        );

    } catch (const std::exception &e) {
        std::cerr << "Exception: " << e.what()  << std::endl;
        return ErrorResponseInfo("Internal exception");
    }
}

